#!/usr/bin/python
# -*- coding:utf-8 -*-
import bson
import datetime
import mongoengine as db

from app.db import BaseDocument, BaseEmbeddedDocument
from app.db.models.assets import TaskSettings as AssetTaskSettings
from app.db.models.wass import ScanEvents
from app.errors import AddJobError
from app.db.models.jobs import (
    AlertSettings,
    VulSettings, SslSettings, SecurityEventSettings,
    ContentSettings, ChangeCheckSettings,
    CustomMonitorPage, PreLogin, Job, HttpSettings, PingSettings
)
from app.db.models.tasks import Task, TaskStatus, TaskSpiderUrl
from app.db.models.users import User
from app.libs.enums import (
    FOREIGN_LINKS_PLUGIN_TUPLE,
    ScanSpeed,
    SPEED_CONCURRENCY_MAP,
    WS_TASK_GROUP_TUPLE,
    AVAILABLE_TASK_GROUP_TUPLE,
    TaskType,
    ASSET_TASK_GROUP_TUPLE,
    TASK_TYPE_CONFIG,
    AssetTaskGroup,
    TaskTriggerType
)
from app.libs.score import Score
from app.config.settings import SPIDER_URL_TIME


class HostVulSettingCollect(BaseEmbeddedDocument):
    pass


class HostVulSettingAlert(BaseEmbeddedDocument):
    """
    "alert":{
            "enable":true
        }

    """
    enable = db.BooleanField(default=True)
    meta = {
        'strict': False
    }


class HostVulSettings(BaseEmbeddedDocument):
    interval = db.LongField(min_value=0, default=86400000)
    timeout = db.LongField(min_value=0, default=2)
    network_scan_profile = db.IntField(min_value=0, max_value=4, default=0)
    is_detect_os_enabled = db.BooleanField(default=False)
    is_detect_port_enabled = db.BooleanField(default=False)
    is_tcp_enabled = db.BooleanField(default=True)
    is_udp_enabled = db.BooleanField(default=False)
    enable = db.BooleanField(default=False)
    taskId = db.StringField(default='')
    collect = db.EmbeddedDocumentField(HostVulSettingCollect, default=HostVulSettingCollect)
    alert = db.EmbeddedDocumentField(HostVulSettingAlert, default=HostVulSettingAlert)


class CustomIpv6Settings(BaseEmbeddedDocument):
    enable = db.BooleanField(default=True)
    taskId = db.StringField(default='')


class WsTaskTime(BaseEmbeddedDocument):
    periodStartTime = db.StringField(default="")
    periodEndTime = db.StringField(default="")
    startAt = db.StringField(default="")


class ScanSpeedConfig(BaseEmbeddedDocument):
    scanSpeed = db.IntField(default=ScanSpeed.AUTO.value)
    concurrency = db.IntField(default=SPEED_CONCURRENCY_MAP[ScanSpeed.AUTO.value])


class AssetTasks(BaseDocument):
    # vul
    vulSettings = db.EmbeddedDocumentField(VulSettings, default=VulSettings)
    vulAlertState = db.DictField(default={"securityStatus": "unknown", "sendCount": 0})
    vulIsStarted = db.BooleanField(default=False)
    vulResultId = db.ObjectIdField()
    vulSecurityStatus = db.StringField(default='unknown')

    # host vul
    hostVulSettings = db.EmbeddedDocumentField(HostVulSettings, default=HostVulSettings)
    hostVulAlertState = db.DictField(default={"securityStatus": "unknown", "sendCount": 0})
    hostVulIsStarted = db.BooleanField(default=False)
    hostVulResultId = db.ObjectIdField()
    hostVulSecurityStatus = db.StringField(default='unknown')

    # ssl
    sslSettings = db.EmbeddedDocumentField(SslSettings, default=SslSettings)
    sslAlertState = db.DictField(default={"securityStatus": "unknown", "sendCount": 0})
    sslIsStarted = db.BooleanField(default=False)
    sslResultId = db.ObjectIdField()
    sslSecurityStatus = db.StringField(default='unknown')

    # securityEvent
    securityEventSettings = db.EmbeddedDocumentField(SecurityEventSettings, default=SecurityEventSettings)
    securityEventAlertState = db.DictField(default={"securityStatus": "unknown", "sendCount": 0})
    securityEventIsStarted = db.BooleanField(default=False)
    securityEventResultId = db.ObjectIdField()
    securityEventSecurityStatus = db.StringField(default='unknown')

    # content
    contentSettings = db.EmbeddedDocumentField(ContentSettings, default=ContentSettings)
    contentAlertState = db.DictField(default={"securityStatus": "unknown", "sendCount": 0})
    contentIsStarted = db.BooleanField(default=False)
    contentResultId = db.ObjectIdField()
    contentPrevResultId = db.ObjectIdField()
    contentSecurityStatus = db.StringField(default='unknown')

    # changeCheck
    changeCheckSettings = db.EmbeddedDocumentField(ChangeCheckSettings, default=ChangeCheckSettings)
    changeCheckAlertState = db.DictField(default={"securityStatus": "unknown", "sendCount": 0})
    changeCheckIsStarted = db.BooleanField(default=False)
    changeCheckResultId = db.ObjectIdField()
    changeCheckSecurityStatus = db.StringField(default='unknown')

    # asset
    assetSettings = db.EmbeddedDocumentField(AssetTaskSettings, default=AssetTaskSettings)
    assetAlertState = db.DictField(default={"securityStatus": "unknown", "sendCount": 0})
    assetIsStarted = db.BooleanField(default=False)
    assetResultId = db.ObjectIdField()
    assetPrevResultId = db.ObjectIdField()
    assetSecurityStatus = db.StringField(default='unknown')

    # http
    httpSettings = db.EmbeddedDocumentField(HttpSettings, default=HttpSettings)
    httpSecurityStatus = db.StringField(default='unknown')
    httpPrevResultId = db.ObjectIdField()
    httpResultId = db.ObjectIdField()
    httpAbnormal = db.BooleanField(default=False)
    httpNextExecDate = db.DateTimeField(default=None)
    httpAlertState = db.DictField(default={"securityStatus": "unknown", "sendCount": 0})

    # ping
    pingSettings = db.EmbeddedDocumentField(PingSettings, default=PingSettings)
    pingSecurityStatus = db.StringField(default='unknown')
    pingPrevResultId = db.ObjectIdField()
    pingResultId = db.ObjectIdField()
    pingAbnormal = db.BooleanField(default=False)
    pingNextExecDate = db.DateTimeField(default=None)
    pingAlertState = db.DictField(default={"securityStatus": "unknown", "sendCount": 0})

    customMonitorPage = db.EmbeddedDocumentField(CustomMonitorPage, default=CustomMonitorPage)
    scanSpeedConfig = db.EmbeddedDocumentField(ScanSpeedConfig, default=ScanSpeedConfig)
    prelogin = db.EmbeddedDocumentField(PreLogin, default=PreLogin)
    sitePortraitId = db.ObjectIdField()

    # ipv6
    ipv6Settings = db.EmbeddedDocumentField(CustomIpv6Settings, default=CustomIpv6Settings)

    name = db.StringField(required=True)
    enableMonitor = db.BooleanField(default=False)
    taskGroupType = db.StringField(required=True, choices=ASSET_TASK_GROUP_TUPLE)
    triggerType = db.StringField(required=True, choices=['schedule', 'manual'])
    taskType = db.StringField()
    targetUrl = db.StringField()

    alertSettings = db.EmbeddedDocumentField(AlertSettings, default=AlertSettings)
    isVerified = db.BooleanField(default=False)
    jobId = db.ObjectIdField(required=True)
    uid = db.ObjectIdField(required=True)
    createTime = db.DateTimeField()
    # web 安全监测任务结果统计数据
    lastResult = db.DictField(default={})
    # web 安全监测任务运行状态
    status = db.StringField(default='waiting', choices=[item.value for item in TaskStatus] + ["unknown", ])
    # 是否源站安全检测任务
    is_defense_task = db.BooleanField(default=False)
    # 运行时间配置
    ws_task_time = db.EmbeddedDocumentField(WsTaskTime, default=WsTaskTime)

    meta = {
        'collection': 'asset_tasks',
        'strict': False
    }

    def clean(self):
        if not self.name:
            raise AddJobError(msg='任务名称不能为空')
        elif len(self.name) > 50:
            raise AddJobError(msg='任务名称不超过50个字符')

        # 兼容安全事件外链plugins
        plugins = self.securityEventSettings.collect.plugins
        if "foreign_links" in plugins:
            plugins.remove("foreign_links")
            plugins.extend(list(FOREIGN_LINKS_PLUGIN_TUPLE))
        return super().clean()

    def save(self, *args, **kwargs):
        if not self.createTime:
            self.createTime = datetime.datetime.utcnow()
        return super(AssetTasks, self).save(*args, **kwargs)

    def is_disabled(self, enable):
        """根据总开关, 认证状态, 和setting的状态, 判断配置是否应该禁用

        Args:
            enable ([type]): setting.enable

        Returns:
            [type]: [description]
        """
        return not (self.enableMonitor and self.isVerified and enable)

    def get_ws_task_ids(self, is_arr=True):
        task_id_map = {}
        for task_type in WS_TASK_GROUP_TUPLE:
            if task_id := getattr(self, f"{task_type}Settings").taskId:
                task_id_map.update({task_type: task_id})
        if is_arr:
            return list(task_id_map.values())
        else:
            return task_id_map

    def get_available_task_ids(self, is_arr=True):
        task_id_map = {}
        for task_type in AVAILABLE_TASK_GROUP_TUPLE:
            if task_id := getattr(self, f"{task_type}Settings").taskId:
                task_id_map.update({task_type: task_id})
        if is_arr:
            return list(task_id_map.values())
        else:
            return task_id_map

    def get_host_task_ids(self, is_arr=True):
        task_id_map = {}
        task_type = TaskType.hostVul.value
        if task_id := getattr(self, f"{task_type}Settings").taskId:
            task_id_map.update({task_type: task_id})
        if is_arr:
            return list(task_id_map.values())
        else:
            return task_id_map

    def get_asset_task_ids(self, is_arr=True):
        task_id_map = {}
        task_type = TaskType.asset.value
        if task_id := getattr(self, f"{task_type}Settings").taskId:
            task_id_map.update({task_type: task_id})
        if is_arr:
            return list(task_id_map.values())
        else:
            return task_id_map

    def get_task_ids(self, asset=None):
        all_task_ids = list()
        all_task_ids.extend(self.get_ws_task_ids())
        all_task_ids.extend(self.get_available_task_ids())
        all_task_ids.extend(self.get_asset_task_ids())
        all_task_ids.extend(self.get_host_task_ids())
        return all_task_ids

    def get_task_ids_v2(self, asset_task=None):
        asset_task = asset_task if asset_task else self
        id_map = dict()
        id_map.update(asset_task.get_ws_task_ids(is_arr=False))
        id_map.update(asset_task.get_available_task_ids(is_arr=False))
        id_map.update(asset_task.get_asset_task_ids(is_arr=False))
        id_map.update(asset_task.get_host_task_ids(is_arr=False))
        return id_map

    def web_security_is_have_result(self):
        if self.taskGroupType != "web_security":
            return False
        for t in WS_TASK_GROUP_TUPLE:
            settings = getattr(self, f"{t}Settings")
            if settings.taskId and settings.enable and getattr(self, f"{t}ResultId"):
                return True
        return False

    def host_is_have_result(self):
        if self.hostVulResultId:
            return True
        return False

    def get_web_security_last_task(self):
        if not self.web_security_is_have_result():
            return {}
        task_ids = []
        for t in WS_TASK_GROUP_TUPLE:
            if task_id := getattr(self, f"{t}ResultId"):
                task_ids.append(task_id)
        return {t.taskType: t for t in Task.objects.filter(id__in=[i for i in task_ids if i])}

    @staticmethod
    def max_score(num, max_num):
        return Score.max_score(num, max_num)

    @staticmethod
    def _score(event_map, warnings):
        return Score._score(event_map, warnings)

    @staticmethod
    def _parse_vul_warnings(warnings, enable=False):
        return Score._parse_vul_warnings(warnings, enable)

    @staticmethod
    def _parse_ssl_warnings(warnings, enable=False):
        return Score._parse_ssl_warnings(warnings, enable)

    @staticmethod
    def _parse_security_event_warnings(warnings, enable=False):
        return Score._parse_security_event_warnings(warnings, enable)

    @staticmethod
    def _get_security_event_severity(warnings):
        return Score.get_security_event_severity(warnings)

    @staticmethod
    def _parse_content_warnings(warnings, enable=False):
        return Score._parse_content_warnings(warnings, enable)

    @staticmethod
    def _parse_change_check_warnings(warnings, enable=False):
        return Score._parse_change_check_warnings(warnings, enable)

    def _get_web_security_last_result(self, task_map=None, is_save=False):
        """最近一次web安全监测漏洞数以及结果"""
        task_map = self.get_web_security_last_task() if not task_map else task_map
        if not task_map:
            return {"score": 0}
        result = {}
        for t, task in task_map.items():
            warnings = task.result.warnings
            target_status = task.result.targetStatus
            # 执行任务的时候资产状态异常，则不计算任务漏洞
            task_error = target_status and target_status.status != "good"
            settings = getattr(self, f"{t}Settings")
            parse_func = getattr(self, f"_parse_{TASK_TYPE_CONFIG[t]['line']}_warnings")
            if (task_error and not warnings) and t != "securityEvent":
                result.update({TASK_TYPE_CONFIG[t]["line"]: {"enable": settings.enable, "score": 0}})
            elif (task_error and not warnings) and t == "securityEvent":
                result.update({
                    "security_event": {"enable": settings.enable, "score": 0},
                    "security_event_severity": {}
                })
            elif (not task_error or warnings) and t == "securityEvent":
                result.update({
                    "security_event": parse_func(warnings, settings.enable),
                    "security_event_severity": self._get_security_event_severity(warnings)
                })
            elif (not task_error or warnings) and t != "securityEvent":
                result.update({TASK_TYPE_CONFIG[t]["line"]: parse_func(warnings, settings.enable)})

        score_list = [item["score"] for _, item in result.items() if "score" in item]
        max_score = max(score_list) if score_list else 0
        is_data = True if result else False
        result.update({"score": max_score})
        if is_data and is_save:
            self.lastResult = result
            self.save()
        return result

    def _save_host_last_result(self):
        """最近一次host监测漏洞数以及结果"""
        if not self.host_is_have_result():
            return {}
        task = Task.objects.filter(id=self.hostVulResultId).first()
        if task.taskSessionId:
            warnings = []
            query = {"task_session_id": task.taskSessionId, "event_name": "network_vuln"}
            host_vul_events = ScanEvents.objects.find(query)
            for event in host_vul_events:
                warnings.append({"level": event.severity})
            score_info = Score.score_info(event_map={}, warnings=warnings)
            result = {"score": score_info.get("score"), "level": score_info.get("level"), "host_vul": score_info}
        else:
            result = {"score": 0, "level": "未知", "host_vul": {}}

        self.lastResult = result
        self.save()

        return result

    def get_web_security_last_result(self, task_map=None):
        """最近一次web安全监测漏洞数以及结果"""
        result = self.lastResult
        if not result:
            result = self._get_web_security_last_result(task_map, True)
        return result

    @staticmethod
    def _get_task_info_with_wass(task_type, task_id):
        task = ScanEvents._get_db()["scan_tasks"].find_one({"task_id": task_id})
        if not task:
            return {}
        res = {
            "end_time": "",
            "next_time": task.get("next_start_at", ""),
            "progress": 0,
            "start_time": "",
            "status": "waiting",
            "taskType": task_type
        }
        return res

    def get_this_per_status(self, last_tasks=None):
        """当前任务每一个监测项的状态"""
        if not last_tasks:
            task_ids = list(self.get_task_ids_v2().values())
            query = [
                {"$match": {"taskId": {"$in": task_ids}}},
                {"$project": {"_id": 1, "taskType": 1, 'endTime': 1, 'status': 1,
                              'progress': 1, "startTime": 1, "nextExecDate": 1}},
                {"$sort": {"_id": -1}},
                {"$group": {"_id": "$taskType", "tasks": {"$push": "$$ROOT"}}},
                {"$project": {"_id": 0, "taskType": "$_id", "tasks": {"$slice": ["$tasks", 1]}}}
            ]
            data = Task._get_collection().aggregate(query)
        else:
            data = last_tasks
        task_group_maps = {at.value: [] for at in AssetTaskGroup}
        for tc in TASK_TYPE_CONFIG.values():
            if tc["at_group"] in task_group_maps:
                task_group_maps[tc["at_group"]].append(tc["line"])
        result = {t: {} for t in task_group_maps[self.taskGroupType] if task_group_maps.get(self.taskGroupType)}
        for d in data:
            task_type = d.pop("taskType")
            item = d["tasks"][0]
            item.pop("_id", "")
            item["start_time"] = item.pop("startTime", "")
            item["end_time"] = item.pop("endTime", "")
            item["next_time"] = item.pop("nextExecDate", "")
            result[TASK_TYPE_CONFIG[task_type]["line"]] = item
        if self.taskGroupType == AssetTaskGroup.web_security.value and self.triggerType == TaskTriggerType.schedule.value:
            task_id_map = {}
            for t in WS_TASK_GROUP_TUPLE:
                if (tid := getattr(self, f"{t}Settings").taskId) and getattr(self, f"{t}Settings").enable:
                    task_id_map.update({t: tid})
            for t in WS_TASK_GROUP_TUPLE:
                if (_id := task_id_map.get(t)) and not result.get(TASK_TYPE_CONFIG[t]["line"]):
                    result[TASK_TYPE_CONFIG[t]["line"]] = self._get_task_info_with_wass(t, _id)
        return result

    def get_this_task_status(self, per_status=None, task_type=None, status=None):
        """监测当前任务整体状态"""
        if task_type and status and task_type not in WS_TASK_GROUP_TUPLE:
            return status
        per_status = per_status if per_status else self.get_this_per_status()
        all_status = {item.get("status"): 1 for _, item in per_status.items() if item}
        if all_status.get("active", 0):
            return "active"
        if all_status.get("waiting", 0):
            return "waiting"
        if all_status.get("stopped", 0):
            return "stopped"
        if all_status.get("completed", 0):
            return "completed"
        return "waiting"

    def update_status(self, per_status=None, task_type=None, status=None):
        """更新任务状态"""
        self.status = self.get_this_task_status(per_status, task_type=task_type, status=status)
        self.save()

    @property
    def job(self):
        return Job.objects.get(id=self.jobId)

    @property
    def task_user(self):
        return User.objects.get(id=self.uid)

    def get_web_security_task_info(self, job=None, user=None, last_tasks=None):
        """
        任务概览信息, 任务列表数据
        """
        job = self.job if not job else job
        user = self.task_user if not user else user
        per_status = self.get_this_per_status(last_tasks)
        data = {
            "asset_task_id": self.id,
            "target_url": job.targetUrl,
            "job_id": job.id,
            "source_ip": job.sourceIp,
            "name": self.name,
            "trigger_type": self.triggerType,
            "monitor_status": per_status,
            "enable_monitor": self.enableMonitor,
            "is_result": self.web_security_is_have_result(),
            "status": self.status,
            "last_result": self.get_web_security_last_result(),
            "username": user.username,
            "uid": self.uid,
            "ws_task_time": self.ws_task_time.to_mongo().to_dict()
        }
        return data

    def get_web_security_task_detail(self):
        """任务详细信息， 任务结果数据"""
        if not self.web_security_is_have_result():
            return {}
        task_map = self.get_web_security_last_task()
        result = self.lastResult or {}
        job = self.job
        user = self.task_user
        overview = {
            "target_url": job.targetUrl,
            "job_name": job.note,
            "name": self.name,
            "asset_task_id": self.id,
            "username": user.username,
            "uid": user.id,
            "trigger_type": self.triggerType,
            "is_display_url": False,
            "ws_task_time": self.ws_task_time.to_mongo().to_dict()
        }
        for t in WS_TASK_GROUP_TUPLE:
            overview.update({
                f"{TASK_TYPE_CONFIG[t]['line']}_result_id": getattr(self, f"{t}ResultId", "")
            })
        response_ip = []
        waf = []
        target_status_list = []
        task_start_times = []
        for task_type, task in task_map.items():
            task_start_times.append(task.startTime)
            _task_type = TASK_TYPE_CONFIG[task_type]["line"]
            _data = result.get(_task_type, {})
            addition = task.result.addition or {}
            statistics = addition.get("statistics", {})
            if waf_list := addition.get("site_info", {}).get("waf_list", []):
                waf.append(waf_list)
            if target_ip := addition.get("target_ip", ""):
                response_ip.append(target_ip)
            if statistics:
                urls = statistics.get("urls", 0) or 0
                scan_count = statistics.get("scan_count", 0) or 0
                _data.update({"url_count": urls if scan_count else 0})
                _data.update({"scan_count": scan_count})
            _warnings = [w.to_dict() for w in task.result.warnings]
            if target_status := task.result.targetStatus:
                target_status = target_status.to_dict()
                _data.update({"target_status": target_status})
                target_status_list.append({"end_time": task.endTime, "target_status": target_status})
            else:
                target_status_list.append({"end_time": task.endTime, "target_status": {"status": "good"}})
                _data.update({"target_status": {"status": "good"}})
            task_settings = task.taskSettings.to_dict()
            security_event_plugins = task_settings.get("collect", {}).get("plugins", [])
            if security_event_plugins and "foreign_links" in security_event_plugins:
                security_event_plugins.remove("foreign_links")
                security_event_plugins.extend(list(FOREIGN_LINKS_PLUGIN_TUPLE))
            _data.update({
                    "warnings": sorted(_warnings, key=lambda x: x["level"], reverse=True),
                    "time_info": self._get_time_info(task),
                    "settings": task_settings,
                    "task_id": task.id,
                    "task_session_id": task.taskSessionId,
                    "cydEnable": job.cydEnabled,
                    "isLastTask": task.isLatestTask,
                    "proxy_ip": task.proxyIp
                })
            result.update({_task_type: _data})
        enable_task_type = []
        all_task_types = list(WS_TASK_GROUP_TUPLE)
        if not job.targetUrl.startswith("https"):
            all_task_types.remove(TaskType.ssl.value)
        for i in all_task_types:
            if getattr(self, f"{i}Settings").enable:
                enable_task_type.append(i)
        overview.update({
            "url_count": len(get_monitor_urls(asset_task_obj=self)),
            "scan_count": get_monitor_urls(asset_task_obj=self, is_scan_count=True),
            "target_ip": "暂无" if not response_ip else response_ip[0],
            "waf_info": "暂无" if not waf else "|".join(waf[0]),
            "enable_task_type": enable_task_type,
            "is_display_url": SPIDER_URL_TIME < min(task_start_times) if task_start_times else False,
        })
        if target_status_list:
            overview.update({
                "target_status": list(sorted(target_status_list, key=lambda x: x["end_time"]))[-1]["target_status"]
            })
        target_status_set = [i["target_status"]["status"] for i in target_status_list]
        score = result.pop("score", 0)
        level = result.pop("level", "未知")
        if score != 0:
            level = [_d["front_CN"] for _, _d in Score.severityMap.items() if _d["score_range"][0] <= score <= _d["score_range"][1]][0]
        elif score == 0 and "good" in target_status_set:
            level = "安全"
        elif score == 0 and "good" not in target_status_set:
            level = "未知"
        overview.update({"score": score, "level": level})
        result.update({"overview": overview})
        return result

    def get_available_task_info(self, job=None, user=None, last_task=None, this_task=None, not_db=False):
        """
        可用性任务概览信息, 任务列表数据
        """
        job = self.job if not job else job
        user = self.task_user if not user else user
        data = {
            "asset_task_id": self.id,
            "target_url": job.targetUrl,
            "job_id": job.id,
            "source_ip": job.sourceIp,
            "name": self.name,
            "status": self.status,
            "trigger_type": self.triggerType,
            "enable_monitor": self.enableMonitor,
            "username": user.username,
            "uid": self.uid,
            "task_group_type": self.taskGroupType,
            "monitor_status": {"status": "", "progress": 0, "start_time": "", "next_time": "", "end_time": ""},
            "last_result_status": "",
            "target": getattr(self, f"{self.taskGroupType}Settings").target,
        }
        result_id = getattr(self, f"{self.taskGroupType}ResultId", None)
        is_result = True if result_id else False
        data.update({"is_result": is_result})
        if is_result and not not_db:
            last_task = last_task if last_task else Task.objects.filter(id=result_id).first()
        if last_task:
            data.update({"last_result_status": last_task.securityStatus,
                         "last_task_id": last_task.id,
                         "last_task_end_time": last_task.endTime})
        if this_task and isinstance(this_task, Task):
            this_task = this_task.to_mongo().to_dict()
        if this_task:
            data["monitor_status"] = {"status": this_task.get("status"),
                                      "progress": this_task.get("progress"),
                                      "start_time": this_task.get("startTime"),
                                      "next_time": this_task.get("nextExecDate"),
                                      "end_time": this_task.get("endTime")}
        return data

    @staticmethod
    def _get_time_info(task: Task):
        diff_time = int((task.endTime - task.startTime).total_seconds())
        one_minute = 60
        oen_hour = one_minute * 60
        one_day = oen_hour * 24
        d = diff_time // one_day
        h = (diff_time - (one_day * d)) // oen_hour
        m = (diff_time - (one_day * d + oen_hour * h)) // one_minute
        s = diff_time - (one_day * d + oen_hour * h + m * one_minute)
        time_list = [d, h, m, s]
        return {"start_time": task.startTime, "end_time": task.endTime, "used_time": time_list}


def get_monitor_urls(
        job_obj=None,
        job_id=None,
        asset_task_obj=None,
        asset_task_id=None,
        task_id=None,
        task_session_id=None,
        is_scan_count=False,
        is_source_data=False
):

    def get_source_data(is_count, session_ids):
        if is_count:
            db_data = ScanEvents._get_db()["scan_pages"].find({"task_session_id": {"$in": session_ids}},
                                                              {"detail.norm_url": 1})
            return len([p.get("detail", {}).get("norm_url") for p in db_data if p.get("detail", {}).get("norm_url")])
        else:
            db_data = ScanEvents._get_db()["scan_spider_urls"].find({"task_session_id": {"$in": session_ids}},
                                                                    {"url": 1})
            return list({p.get("url") for p in db_data if p.get("url")})

    def get_result_data(is_count, session_ids):
        if is_count:
            return sum([_t.result.addition.get("statistics", {}).get("scan_count", 0) or 1 for _t in Task.objects.filter(
                taskSessionId__in=session_ids).only("result.addition.statistics.scan_count")
            ])
        else:
            urls = []
            for _t in TaskSpiderUrl.objects.filter(taskSessionId__in=session_ids):
                urls.extend(_t.spiderUrls or [])
            return list(set(urls))

    task_types = list(WS_TASK_GROUP_TUPLE)
    task_ids = []
    obj = None
    if task_id:
        task_ids.append(bson.ObjectId(task_id))
    elif job_obj:
        obj = job_obj
    elif job_id:
        obj = Job.objects.filter(id=job_id).first()
    elif asset_task_obj:
        obj = asset_task_obj
    elif asset_task_id:
        obj = AssetTasks.objects.filter(id=asset_task_id).first()
    if obj:
        for t in task_types:
            if _ := getattr(obj, f"{t}ResultId", None):
                task_ids.append(_)
    if task_ids and (tasks := Task.objects.filter(id__in=task_ids, taskType__in=task_types).only("taskSessionId")):
        task_session_ids = [t.taskSessionId for t in tasks]
    elif task_session_id:
        task_session_ids = [task_session_id, ] if not isinstance(task_session_id, list) else task_session_id
    else:
        return [] if not is_scan_count else 0
    if is_source_data:
        return get_source_data(is_scan_count, task_session_ids)
    else:
        return get_result_data(is_scan_count, task_session_ids)
